#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PyQt4.QtCore       import *
from PyQt4.QtGui        import *

import time

g_nimages_pre_load = 8

class PreDecodeThread(QThread):
    def __init__(self):
        QThread.__init__(self)

        self.mutex = QMutex()
        self.cond  = QWaitCondition()

        # get thread setup
        self.file_name_list        = []
        self.expect_pos            = 0
        self.file_name_list_change = False

        # decode thread setup
        self.loaded_images = []
        self.pos           = 0

    def change_file_name_list(self, file_name_list):
        self.mutex.lock()

        if len(self.file_name_list) == 0:
            self.file_name_list_change = False
        else:
            self.file_name_list_change = True
        self.file_name_list = file_name_list
        self.loaded_images  = []
        self.expect_pos     = 0
        self.pos            = 0

        self.mutex.unlock()

        self.cond.wakeAll()

    def get_image(self, pos):
        """
        
        Arguments:
        - `self`:
        """
        image = None
        
        self.mutex.lock()

        self.expect_pos = pos

        llen = len(self.loaded_images)

        if pos >= self.pos and pos < (llen + self.pos):
            image = self.loaded_images[pos - self.pos]
        else:
            while True:
                self.cond.wait(self.mutex)

                llen = len(self.loaded_images)
                if pos >= self.pos and pos < (llen + self.pos):
                    image = self.loaded_images[pos - self.pos]
                    break
        # print "%d (%d - %d)" % (pos, self.pos, self.pos + len(self.loaded_images))
        self.mutex.unlock()

        return image

    def run(self):
        """
        
        Arguments:
        - `self`:
        """
        forward_pred    = True
        prev_expect_pos = 0
        while True:
            self.mutex.lock()

            if self.expect_pos != prev_expect_pos:
                if self.expect_pos > prev_expect_pos:
                    forward_pred = True
                else:
                    forward_pred = False
                prev_expect_pos = self.expect_pos

            flen  = len(self.file_name_list)

            while flen == 0:
                self.cond.wait(self.mutex)
                flen = len(self.file_name_list)

            half  = g_nimages_pre_load / 2
            right = min(flen, self.expect_pos + half)
            left  = max(0,    self.expect_pos - half)

            llen = len(self.loaded_images)

            # delete the old cached image
            if self.pos < left:
                diff = left - self.pos
                if diff > llen:
                    diff = llen
                self.loaded_images = self.loaded_images[diff:]
                llen = len(self.loaded_images)
                self.pos = left
                # print "free left %d" % diff

            if self.pos + llen > right:
                diff = self.pos + llen - right
                if diff > llen:
                    diff = llen
                self.loaded_images = self.loaded_images[0:llen - diff]
                llen = len(self.loaded_images)
                # print "free right %d" % diff

            beg  = self.pos
            end  = beg + llen

            load_idx = None
            if forward_pred:
                if end < right:
                    load_idx = end
            if load_idx is None:
                if beg > left:
                    load_idx = beg - 1

            if load_idx is None:
                self.mutex.unlock()
                # 50ms
                time.sleep(0.05)
                continue

            fn = self.file_name_list[load_idx]

            self.mutex.unlock()

            # print "loading %s [%d]..." % (fn, load_idx)
            image = QImage(fn)

            # assignment
            self.mutex.lock()
            if self.file_name_list_change:
                self.file_name_list_change = False
            else:
                if load_idx == end:
                    self.loaded_images.append(image)
                else:
                    self.loaded_images.insert(0, image)
                    self.pos -= 1

            # print " > loaded %d %d" % (self.pos, len(self.loaded_images))
            self.mutex.unlock()

            self.cond.wakeAll()


if __name__ == '__main__':
    decodeThread = PreDecodeThread()
    decodeThread.start()

    images = []

    import os
    import time
    for fn in os.listdir("."):
        if fn.lower().endswith(".jpg"):
            images.append(fn)

    decodeThread.change_file_name_list(images)

    flen = len(images)

    print "got %d images" % flen
    cur = 0

    while True:
        assert decodeThread.get_curr_image() is not None
        time.sleep(2)
        cur += 1
        if cur >= flen:
            break
        decodeThread.change_seek_pos(cur)

    decodeThread.terminate()
